package org.zoomdev.zoom.lock.modules;

import org.zoomdev.zoom.common.Destroyable;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.lock.KeyedObjectPool;
import org.zoomdev.zoom.lock.PoolException;

import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 默认对象池，如果情况简单可以用这个
 *
 * @param <T>
 */
abstract class DefaultKeyedObjectPool<T> implements KeyedObjectPool<T>, Destroyable {

    private final Map<String, Holder<T>> keys = new ConcurrentHashMap<>();

    /**
     * 空闲
     */
    private final Queue<Holder<T>> free = new LinkedList<>();


    private final int maxTotal;

    private int used;


    public DefaultKeyedObjectPool() {
        this(Integer.MAX_VALUE);
    }

    public DefaultKeyedObjectPool(int maxTotal) {
        this.maxTotal = maxTotal;
    }

    static class Holder<T> {
        T data;

        int ref;
    }

    public void destroy() {
        free.clear();
    }

    public T fetch(String key) {
        return keys.compute(key, (k, v) -> {
            if (v == null) {
                return create(k);
            }
            v.ref++;
            return v;
        }).data;
    }


    protected synchronized Holder<T> create(String key) {
        if (!free.isEmpty()) {
            Holder<T> holder = free.remove();
            if (holder == null) {
                throw new ZoomException("holder is null");
            }
            ++used;
            holder.ref++;
            initObject(holder.data, key);
            return holder;
        }
        if (used >= maxTotal) {
            //等待
            throw new PoolException("对象池已满，不能创建更多对象");
        }
        T data = createObject(key);
        Holder<T> holder = new Holder<>();
        holder.data = data;
        holder.ref = 1;
        ++used;
        return holder;
    }

    /**
     * 取出空闲对象的时候调用
     */
    protected abstract void initObject(T data, String key);

    /**
     * 创建对象
     */
    protected abstract T createObject(String key);

    public T get(String key) {
        Holder<T> holder = keys.get(key);
        if (holder == null) {
            return null;
        }
        return holder.data;
    }

    public void release(String key, T data) {
        keys.compute(key, (k, v) -> {
            if (v == null) {
                return null;
            }

            if (v.data == data) {
                --v.ref;
            }

            if (v.ref == 0) {
                v.data = clearObject(v.data, k);
                put(k, v);
                return null;
            }
            return v;
        });
    }

    public int getUsed() {
        return used;
    }

    protected abstract T clearObject(T data, String key);

    protected synchronized void put(String key, Holder<T> holder) {
        --used;
        if (free.size() < maxTotal) {
            free.add(holder);
        } else {
            destroyObject(key, holder.data);
        }
    }

    /**
     * 销毁对象
     */
    protected void destroyObject(String key, T data) {

    }

}
